package net.chenlin.dp.modules.socket;

import lombok.extern.slf4j.Slf4j;
import net.chenlin.dp.modules.business.entity.po.CalibrationMessagePo;
import net.chenlin.dp.modules.business.entity.po.DataMessagePo;
import net.chenlin.dp.modules.business.entity.po.DeviceRegisterPo;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Yangsm
 * @ClassName: KeepServer
 * @Description: TODO
 * @date 2022年11月16日 22:54
 */
@Slf4j
public class KeepServer {

    /**
     * 要处理客户端发来的对象，并返回一个对象，可实现该接口。
     */
    public interface ObjectAction {
        Object doAction(Object rev, KeepServer server);
    }

    public static final class DefaultObjectAction implements KeepServer.ObjectAction {
        @Override
        public Object doAction(Object rev, KeepServer server) {
            System.out.println("Server返回发送给Client的消息：" + rev);
            return rev;
        }
    }

    public static void main(String[] args) {
        int port = 10003;
        KeepServer server = new KeepServer(port);
        server.start();
    }

    private int port;

    private volatile boolean running = false;

    //private long receiveTimeDelay = 10000;

    private ConcurrentHashMap<Class, KeepServer.ObjectAction> actionMapping = new ConcurrentHashMap<Class, KeepServer.ObjectAction>();

    private Thread connWatchDog;

    public KeepServer(int port) {
        this.port = port;
    }

    public void start() {
        if (running) {
            return;
        }
        running = true;
        connWatchDog = new Thread(new KeepServer.ConnWatchDog());
        connWatchDog.start();
        System.out.println("Server服务端启动成功");
    }

    @SuppressWarnings("deprecation")
    public void stop() {
        if (running) {
            running = false;
        }
        if (connWatchDog != null) {
            connWatchDog.stop();
        }
    }

    public void addActionMap(Class<Object> cls, KeepServer.ObjectAction action) {
        actionMapping.put(cls, action);
    }

    class ConnWatchDog implements Runnable {

        @Override
        public void run() {
            System.out.println("ConnWatchDog的run方法。。。。。。");
            try {
                ServerSocket ss = new ServerSocket(port, 5);
                while (running) {

                    System.out.println("客户端来消息了. . .");
                    //连接请求队列中取出一个客户的连接请求，然后创建与客户连接的Socket对象，并将它返回。
                    // 如果队列中没有连接请求，accept()方法就会一直等待，直到接收到了连接请求才返回
                    Socket clientSocket = ss.accept();
                    log.info("=== 有客户端连上服务端, 客户端信息如下：" + clientSocket.getInetAddress() + " : " + clientSocket.getPort() + ".");

                    new Thread(new SocketAction(clientSocket)).start();

                }
            } catch (Exception e) {
                e.printStackTrace();
                log.info("发生异常，服务关闭");
                KeepServer.this.stop();
            }

        }
    }

    class SocketAction implements Runnable {
        Socket s;
        boolean run = true;
        long lastReceiveTime = System.currentTimeMillis();

        public SocketAction(Socket s) {
            this.s = s;
        }

        @Override
        public void run() {
            while (running && run) {
                try {
                    InputStream inputStream = s.getInputStream();
                    if (inputStream.available() > 0) {

                        // 客户端发过来的消息
                        byte[] by = new byte[1024];
                        int len = inputStream.read(by);
                        String data = new String(by, 0, len);
                        System.out.println("客户端发来的消息是：== " + data);
                        log.info("=== 客户端发来的消息的地址是：" + s.getInetAddress() + " : " + s.getPort() + ".");
                        int length = data.length();
                        int length1 = data.trim().length();
                        System.out.println(length == length1);
                        System.out.println("trim之后的长度是否相等：== " + length + " == " + length1);
                        data = data.trim();
                        //lastReceiveTime = System.currentTimeMillis();
                        //System.out.println();
                        boolean endsWith = data.endsWith("}");
                        System.out.println("是否是已}结尾：" + endsWith);

                        if (endsWith) {
                            String substring = data.substring(1, data.length() - 1);
                            String[] split = substring.split(",");
                            String s = split[split.length - 1];
                            System.out.println("split截取最后一截是：" + s);
                            boolean res = split[split.length - 1].equals("#");
                            boolean res2 = split[split.length - 1].equals("REG");
                            log.info("结果1是：" + res);
                            log.info("结果2是：" + res2);

                            String first = split[0];
                            log.info("split截取第一截是：" + first);

                            // 是否是心跳报文
                            if (split[split.length - 1].equals("#")) {

                                if(split[split.length - 1].equals("#") && split[0].equals("JZALIVE")){

                                    // 回复心跳报文（校准报文心跳）
                                    SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");
                                    String dateString = sf.format(new Date());
                                    String str = "{" + split[0] + "," + dateString + ",$}";

                                    OutputStream os = this.s.getOutputStream();
                                    os.write(str.getBytes());
                                    System.out.println("回复心跳报文：" + str);

                                    // 修改设备心跳维持时间
                                    String timeString = split[1];
                                    String year = timeString.substring(0, 4);
                                    String month = timeString.substring(4, 6);
                                    String day = timeString.substring(6, 8);
                                    String hour = timeString.substring(8, 10);
                                    String minute = timeString.substring(10, 12);
                                    String second = timeString.substring(12, 14);
                                    String time = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
                                    String deviceNo = split[0];

                                    // jdbc
                                    Class.forName("com.mysql.cj.jdbc.Driver");
                                    String url = "jdbc:mysql://139.9.159.254:3306/yidong-dianbiao";
                                    Connection conn = DriverManager.getConnection(url, "root", "dianbiao@123");
                                    Statement st = conn.createStatement();
                                    String sql = "UPDATE device_register SET heartbeat_time = '" + time + "' WHERE device_no = '" + deviceNo + "';";
                                    System.out.println(sql);
                                    st.executeUpdate(sql);//调用函数执行sql语句

                                }else{

                                    // 回复心跳报文（设备心跳）
                                    SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");
                                    String dateString = sf.format(new Date());
                                    String str = "{" + split[0] + "," + dateString + ",$}";

                                    OutputStream os = this.s.getOutputStream();
                                    os.write(str.getBytes());
                                    System.out.println("设备回复心跳报文：" + str);

                                    // 修改设备心跳维持时间
                                    String timeString = split[1];
                                    String year = timeString.substring(0, 4);
                                    String month = timeString.substring(4, 6);
                                    String day = timeString.substring(6, 8);
                                    String hour = timeString.substring(8, 10);
                                    String minute = timeString.substring(10, 12);
                                    String second = timeString.substring(12, 14);
                                    String time = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
                                    String deviceNo = split[0];

                                    // jdbc
                                    Class.forName("com.mysql.cj.jdbc.Driver");
                                    String url = "jdbc:mysql://139.9.159.254:3306/yidong-dianbiao";
                                    Connection conn = DriverManager.getConnection(url, "root", "dianbiao@123");
                                    Statement st = conn.createStatement();
                                    //String sql = "UPDATE device_register SET heartbeat_time = '" + time + "' WHERE device_no =" + deviceNo + ";";
                                    String sql = "UPDATE device_register SET heartbeat_time = '" + time + "' WHERE device_no = '" + deviceNo + "';";
                                    System.out.println(sql);
                                    st.executeUpdate(sql);//调用函数执行sql语句

                                }

                            } else if (split[split.length - 1].equals("REG")) {

                                if(split[0].equals("JZCLIENT")){

                                    // 回复校准终端激活报文（校准报文注册）
                                    SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");
                                    String dateString = sf.format(new Date());
                                    String str = "{" + split[0] + "," + dateString + ",SUC}";
                                    OutputStream os = this.s.getOutputStream();
                                    os.write(str.getBytes());
                                    System.out.println("回复校准终端激活报文：" + str);

                                }else{

                                    // 回复终端激活报文（之前的普通设备）
                                    SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmss");
                                    String dateString = sf.format(new Date());
                                    //System.out.println(dateString);
                                    String str = "{" + split[0] + "," + dateString + ",SUC}";
                                    System.out.println(str);

                                    OutputStream os = this.s.getOutputStream();
                                    os.write(str.getBytes());
                                    System.out.println("回复终端激活报文：" + str);

                                    // 收到设备发出的tcp激活报文在设备登记表创建设备信息

                                    DeviceRegisterPo po = new DeviceRegisterPo();

                                    po.setDeviceNo(split[0]);
                                    String timeString = split[1];
                                    String year = timeString.substring(0, 4);
                                    String month = timeString.substring(4, 6);
                                    String day = timeString.substring(6, 8);
                                    String hour = timeString.substring(8, 10);
                                    String minute = timeString.substring(10, 12);
                                    String second = timeString.substring(12, 14);
                                    String time = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
                                    Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(time);
                                    po.setCreatTime(time);
                                    po.setDeviceStatus(0);

                                    // jdbc
                                    Class.forName("com.mysql.cj.jdbc.Driver");
                                    String url = "jdbc:mysql://139.9.159.254:3306/yidong-dianbiao";
                                    Connection conn = DriverManager.getConnection(url, "root", "dianbiao@123");
                                    Statement st = conn.createStatement();
                                    // TODO: 2023/4/6 判断谁被是否存在，存在就直接恢复在线状态
                                    String querySql = "SELECT count(1) AS num FROM device_register WHERE device_no ="+po.getDeviceNo();
                                    ResultSet resultSet = st.executeQuery(querySql);
                                    List<Map<String, Integer>> list = new ArrayList<>();
                                    ResultSetMetaData metaData = resultSet.getMetaData();
                                    int columnCount = metaData.getColumnCount();
                                    while (resultSet.next()) {
                                        Map<String, Integer> jsonMap = new HashMap<>();
                                        for (int i = 1; i <= columnCount; i++) {
                                            String columnTypeName = metaData.getColumnTypeName(i);
                                            String columnName = metaData.getColumnName(i);
                                            if ("INT".equals(columnTypeName)) {
                                                int anInt = resultSet.getInt(columnName);

                                                jsonMap.put(columnName, anInt);
                                            } else {
                                                String cloumValue = resultSet.getString(columnName);
                                                jsonMap.put(columnName, Integer.valueOf(cloumValue));
                                            }
                                        }
                                        list.add(jsonMap);
                                    }
                                    int num = list.get(0).get("num");
                                    if(num == 0){
                                        String sql = "INSERT INTO device_register " +
                                                "(device_no," +
                                                "device_status," +
                                                "creat_time" +
                                                ")" +
                                                "VALUES" +
                                                "('" + po.getDeviceNo() + "'," + "'" + po.getDeviceStatus() + "','" + po.getCreatTime() + "');";
                                        System.out.println(sql);
                                        st.executeUpdate(sql);//调用函数执行insert语句
                                    }

                                }

                            } else if (split[1].equals("SETIPOK")) {

                                // 设置IP的指令
                                String str = "{" + split[0] + ",SETIP,139,9,159,254,8090}";
                                System.out.println(str);

                                OutputStream os = this.s.getOutputStream();
                                os.write(str.getBytes());
                                System.out.println("回复设置IP的指令：" + str);

                            }else if(split[0].equals("JZDB")){//校准电表 - 电脑客户端上行（客户端至后台）

                                CalibrationMessagePo po = new CalibrationMessagePo();

                                // 获取TCP传过来的报文时间
                                String timeString = split[1];
                                String year = timeString.substring(0, 4);
                                String month = timeString.substring(4, 6);
                                String day = timeString.substring(6, 8);
                                String hour = timeString.substring(8, 10);
                                String minute = timeString.substring(10, 12);
                                String second = timeString.substring(12, 14);
                                String dateTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
                                //Date dateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(time);
                                po.setDateTime(dateTime);
                                po.setFifteenMinutesPower(split[2]);
                                po.setCountPower(split[3]);

                                // jdbc
                                Class.forName("com.mysql.cj.jdbc.Driver");
                                String url = "jdbc:mysql://139.9.159.254:3306/yidong-dianbiao?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true";
                                Connection conn = DriverManager.getConnection(url, "root", "dianbiao@123");
                                Statement st = conn.createStatement();

                                String sql = "INSERT INTO calibration_message " +
                                        "(date_time," +
                                        "fifteen_minutes_power," +
                                        "count_power," +
                                        "accept_time" +
                                        ")" +
                                        "VALUES" +
                                        "('" + po.getDateTime() + "','" + po.getFifteenMinutesPower() + "','" + po.getCountPower() + "'," + "NOW()" + ");";
                                System.out.println("基准电表报文数据sql ====== "+sql);
                                st.executeUpdate(sql);//调用函数执行insert语句

                            } else {
                                // 解析数据报文
                                // System.out.println(" ============== ");
                                DataMessagePo po = new DataMessagePo();
                                po.setDeviceId(split[0]);
                                //po.setCommCountIndex(Integer.valueOf(split[1]));
                                String timeString = split[1];
                                String year = timeString.substring(0, 4);
                                String month = timeString.substring(4, 6);
                                String day = timeString.substring(6, 8);
                                String hour = timeString.substring(8, 10);
                                String minute = timeString.substring(10, 12);
                                String second = timeString.substring(12, 14);
                                String time = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
                                Date endDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(time);
                                Calendar calendar = Calendar.getInstance();
                                calendar.setTime(endDate);
                                calendar.add(Calendar.MINUTE, -15);
                                Date startDate = calendar.getTime();
                                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                                String format = dateFormat.format(startDate);
                                po.setMeasurementStartTime(startDate);
                                po.setMeasurementEndTime(endDate);
                                po.setOneChannelMinutePower(split[2]);
                                po.setOneChannelAccumulatedPower(split[3]);
                                po.setTwoChannelMinutePower(split[4]);
                                po.setTwoChannelAccumulatedPower(split[5]);
                                po.setThreeChannelMinutePower(split[6]);
                                po.setThreeChannelAccumulatedPower(split[7]);

                                // jdbc
                                Class.forName("com.mysql.cj.jdbc.Driver");
                                String url = "jdbc:mysql://139.9.159.254:3306/yidong-dianbiao?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowMultiQueries=true";
                                Connection conn = DriverManager.getConnection(url, "root", "dianbiao@123");
                                Statement st = conn.createStatement();

                                String querySql = "SELECT MAX(id) as num FROM data_message WHERE device_no ="+po.getDeviceId();
                                ResultSet resultSet = st.executeQuery(querySql);

                                List<Map<String, Integer>> list = new ArrayList<>();
                                ResultSetMetaData metaData = resultSet.getMetaData();
                                int columnCount = metaData.getColumnCount();
                                while (resultSet.next()) {
                                    Map<String, Integer> jsonMap = new HashMap<>();
                                    for (int i = 1; i <= columnCount; i++) {
                                        String columnTypeName = metaData.getColumnTypeName(i);
                                        String columnName = metaData.getColumnName(i);
                                        if ("INT".equals(columnTypeName)) {
                                            int anInt = resultSet.getInt(columnName);

                                            jsonMap.put(columnName, anInt);
                                        } else {
                                            String result = resultSet.getString(columnName);
                                            jsonMap.put(columnName, Integer.valueOf(result));
                                        }
                                    }
                                    list.add(jsonMap);
                                }
                                int num = list.get(0).get("num");
                                int id = 1;
                                if(num != 0 ){
                                    id = num +1;
                                }

                                String sql = "INSERT INTO data_message " +
                                        "(device_no," +
                                        "id," +
                                        "one_channel_minute_power," +
                                        "one_channel_accumulated_power," +
                                        "two_channel_minute_power," +
                                        "two_channel_accumulated_power," +
                                        "three_channel_minute_power," +
                                        "three_channel_accumulated_power," +
                                        "measurement_start_time," +
                                        "measurement_end_time," +
                                        "creat_time" +
                                        ")" +
                                        "VALUES" +
                                        "('" + po.getDeviceId() + "'," + id + "," + po.getOneChannelMinutePower() + "," + po.getOneChannelAccumulatedPower() + "," +
                                        po.getTwoChannelMinutePower() + "," + po.getTwoChannelAccumulatedPower() + "," + po.getThreeChannelMinutePower() + "," +
                                        po.getThreeChannelAccumulatedPower() + ",'" + format + "','" + time + "'," + "NOW()" + ");";
                                //System.out.println(sql);
                                st.executeUpdate(sql);//调用函数执行insert语句
                            }
                        }
                        //s.close();

                    } else {
                        Thread.sleep(10);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println(" 代码发生异常，关闭TCP连接");
                    //overThis();
                }
//                }
            }
        }

        private void overThis() {
            if (run) {
                run = false;
            }
            if (s != null) {
                try {
                    s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("关闭TCP连接：" + s.getRemoteSocketAddress());
        }

    }
}
